﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Minestorm.Retro.GameLogic;

namespace Minestorm.Retro.Entities
{
    public class PointSpriteGenerator
    {
        private EntityManager _entityManager;
        private Vector2 _center;
        private float _radiusX;
        private float _radiusY;
        private float _centerRadiusX;
        private float _centerRadiusY;
        private float _centerRadiusVariance;
        private bool _towardsCenter;
        private SpriteBatch _spriteBatch;
        private TextureManager _textureManager;
        private bool _started;
        private TimeSpan _ttl;
        private int _spriteTtl;
        private TimeSpan _startedOn;
        private GraphicsDevice _graphics;
        private int _maxSpritesPerUpdate;
        private int _spriteTtlVariance;

        public PointSpriteGenerator(GraphicsDevice graphics, TextureManager textureManager, EntityManager entityManager)
        {
            _entityManager = entityManager;
            _spriteBatch = new SpriteBatch(graphics);
            _textureManager = textureManager;
            _graphics = graphics;
        }

        public void Start(Vector2 center, int maxSpritesPerUpdate, float centerRadiusX, float centerRadiusY, float centerRadiusVariance, float radiusX, float radiusY, bool towardsCenter, TimeSpan ttl, int spriteTtl, int spriteTtlVariance, TimeSpan startedOn)
        {
            _center = center;
            _radiusX = radiusX;
            _radiusY = radiusY;
            _towardsCenter = towardsCenter;
            _centerRadiusX = centerRadiusX;
            _centerRadiusY = centerRadiusY;
            _centerRadiusVariance = centerRadiusVariance;
            _started = true;
            _ttl = ttl;
            _spriteTtl = spriteTtl;
            _startedOn = startedOn;
            _maxSpritesPerUpdate = maxSpritesPerUpdate;
            _spriteTtlVariance = spriteTtlVariance;
        }

        public void Stop()
        {
            _started = false;
        }

        public void Update(GameTime gameTime)
        {
            if (!_started)
                return;

            if (gameTime.TotalGameTime - _startedOn > _ttl)
            {
                _started = false;
                return;
            }

            var count = RandomHelper.Random.Next(0, _maxSpritesPerUpdate + 1);

            for (var i = 0; i < count; i++)
            {
                var angle = RandomHelper.GetAngle();

                var cRadiusVariance = RandomHelper.Random.NextDouble() * _centerRadiusVariance;

                if (RandomHelper.Bool())
                    cRadiusVariance = -cRadiusVariance;

                var cRadiusX = _centerRadiusX + cRadiusVariance;
                var cRadiusY = _centerRadiusY + cRadiusVariance;

                Vector2 start = new Vector2(_center.X + (float)Math.Sin(-angle) * _radiusX, _center.Y + (float)Math.Cos(-angle) * _radiusY);
                Vector2 end = new Vector2(_center.X + (float)Math.Sin(-angle) * _centerRadiusX, _center.Y + (float)Math.Cos(-angle) * _centerRadiusY);

                if (!_towardsCenter)
                {
                    var tmp = start;
                    start = end;
                    end = tmp;
                }

                var ttl = TimeSpan.FromMilliseconds(RandomHelper.Random.Next(_spriteTtl - _spriteTtlVariance, _spriteTtl + _spriteTtlVariance));

                var s = new PointSprite(_graphics, start, end, _textureManager.GetTexture("pointsprite"), _spriteBatch, ttl);

                _entityManager.AddEntity(s);
            }

            foreach (var sprite in _entityManager.GetPointSprites())
                sprite.Update(gameTime);
        }

        public void Draw(GameTime gameTime)
        {
            if (!_started)
                return;

            _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);

            foreach (var sprite in _entityManager.GetPointSprites())
                sprite.Draw(gameTime);

            _spriteBatch.End();
        }
    }

    public class PointSprite : Entity
    {
        private Texture2D _texture;
        private SpriteBatch _spriteBatch;
        private GraphicsDevice _graphics;
        private TimeSpan _ttl;
        private Vector2 _direction;

        public PointSprite(GraphicsDevice graphics, Vector2 startPosition, Vector2 endPosition, Texture2D texture, SpriteBatch spriteBatch, TimeSpan ttl)
            : base(graphics, startPosition)
        {
            _graphics = graphics;
            _texture = texture;
            _direction = Vector2.Normalize(endPosition - startPosition) * .1f;
            _spriteBatch = spriteBatch;
            _ttl = ttl;
        }

        public override void Initialize()
        {
        }

        public override Matrix GetWorld(string indexBufferName)
        {
            return Matrix.Identity * Matrix.CreateTranslation(new Vector3(Position, 0));
        }

        public override void Update(GameTime gameTime)
        {
            if (gameTime.TotalGameTime - BornOn > _ttl)
            {
                ShouldDraw = false;
                return;
            }

            Position += _direction;
        }

        public override void Draw(GameTime gameTime)
        {
            if (!ShouldDraw)
                return;

            var vec = _graphics.Viewport.Project(Vector3.Zero, Level.Projection, Level.View, GetWorld(null));

            _spriteBatch.Draw(_texture, new Rectangle((int)vec.X - (_texture.Width / 2), (int)vec.Y - (_texture.Height / 2), _texture.Width, _texture.Height), Constants.VectorColor);
        }
    }
}
